/*
  Copyright (c) 2010 Les Newell. All rights reserved

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SOCKETS_H_INCLUDED
#define SOCKETS_H_INCLUDED
#include "common.h"
#include "module.h"


//this file us UGLY. Unfortunately we can't use templates.


///Class that exposes data to other modules

/**Sockets are the main method of communication between modules.
Sockets can be read or written by other modules.
They can be used pretty much like their data type. For instance
Socket16 behaves to all intents and purposes like a short int.
To expose sockets to other modules, use this syntax in your module's constructor:
\code AddSocket("socket name", &socket);\endcode

Memory consumption of a socket or parameter is 2 bytes plus payload on von Neumann architecures.
On Harvard architectures such as AVR, ram conumption may be much higher if the names are placed in RAM.

Very little if any CPU overhead, assuming the compiler does it's job properly.
*/


class Socket
{
public:
///Print the contents of the socket to the given print destination
    virtual void Print(class Print& dest)
    {
        dest.print("Type not defined");
    }

///Is this socket a parameter?
/**Parameters are special sockets that are storted in EEPROM
*/
    virtual bool IsParameter()
    {
        return (false);
    }

    //These functions are only used by parameters.
    //It is ugly to define them here but there is nothing I can do about it

///Convert a string to the parameter's data type
    virtual bool Parse(const char * string)
    {
        return(false);
    }

///Get raw data pointer and size.
/**Used mainly for backing up to non volatile memory
*/
    virtual void * GetPayload(int& size)
    {
        Errorln("No payload");
        return(NULL);
    }

///Get the payload type name
/**mainly used by the module manager
*/
    virtual const char * GetType()
    {
        Errorln("Type not defined");
        return("");
    };
};


#ifdef DOXYDOC //a fudge to document the classes created from the following #define

///Sockets of various types
/**
Currently defined are:
\li Socket8 (8 bit unsigned)
\li Socket16 (16 bit signed)
\li Socket32 (32 bit signed)
\li SocketF (Floating point)
*/

template<class T>
class SocketT : public Socket
{
  public:
///Get the socket value
    inline operator T() const;
///Get a pointer to the payload data
/**
Returns a pointer to the underlying data type.
*/
    inline T* GetPointer(){return(&m_data);}
///Assignment
    inline const T& operator = (const T value);
///Assignment by addition
    inline const T& operator += (const T value);
///Assignment by subtraction
    inline const T& operator -= (const T value);
///Assignment by multiplication
    inline const T& operator *= (const T value);
///Assignment by division
    inline const T& operator /= (const T value);
///Get the class type name
/** For example Socket8 would return "Socket8"
*/
    virtual const char * GetType();
    virtual void Print(class Print& dest);
///Get the underlying data as raw memory
/**Returns a pointer to the data and sets size to the size of the data
*//
    virtual void * GetPayload(int& size);
  protected:\
    T m_data;\
}
#endif


#define DEFINE_SOCKET(C,T,P,D)\
  class C : public Socket\
  {\
  public:\
    C(){m_data = 0;}\
    inline operator T() const {return (m_data);}\
    inline T* GetPointer(){return(&m_data);}\
    inline const T& operator = (const T value){m_data = value; return m_data;}\
    inline const T& operator += (const T value){m_data += value; return m_data;}\
    inline const T& operator -= (const T value){m_data -= value; return m_data;}\
    inline const T& operator *= (const T value){m_data *= value; return m_data;}\
    inline const T& operator /= (const T value){m_data /= value; return m_data;}\
    virtual const char * GetType(){return(#C);}\
    virtual void Print(class Print& dest){dest.print((P)m_data,D);}\
    virtual void * GetPayload(int& size){size = sizeof(T);return(&m_data);}\
  protected:\
    T m_data;\
  };


//define classes Socket8 (8 bit), Socket16(16 bit), Socket32(32 bit) and SocketF(float)

DEFINE_SOCKET(Socket8,byte,int,DEC);
DEFINE_SOCKET(Socket16,short,short,DEC);
DEFINE_SOCKET(Socket32,long,long,DEC);
DEFINE_SOCKET(SocketF,float,float,6);
#undef DEFINE_SOCKET


#ifdef DOXYDOC //a fudge to document the classes created from the following #define

///Parameters of various types
/**

Parameters are like non-volatile sockets. They are stored in EEPROM
They can be read by other modules. Writing to them is a special case
and has to be done via ModuleManager.
Have look at the serial module for an example.

Currently defined are:
\li Parameter8 (8 bit unsigned)
\li Parameter16 (16 bit signed)
\li Parameter32 (32 bit signed)
\li ParameterF (Floating point)
*/

template<class T>
class ParameterT : public SocketT
{
  public:
///Get the parameter value
    inline operator T() const;
///Get a pointer to the payload data
/**
Returns a pointer to the underlying data type.
*/
    inline T* GetPointer(){return(&m_data);}
///Assignment
    inline const T& operator = (const T value);
///Assignment by addition
    inline const T& operator += (const T value);
///Assignment by subtraction
    inline const T& operator -= (const T value);
///Assignment by multiplication
    inline const T& operator *= (const T value);
///Assignment by division
    inline const T& operator /= (const T value);
    virtual const char * GetType();
    virtual void Print(class Print& dest);
    virtual void * GetPayload(int& size);
///Returns true if this is a parameter
    virtual bool IsParameter(){return (true);}
///Parse a string value
/**The string value is converted to the parameters's data type.

For instance a ParameterF would change it's value to the value string.
*/
    virtual bool Parse(const char * string){m_data = P(string);}
}
#endif


#define DEFINE_PARAMETER(C,T,D,P)\
  class C : public D\
  {\
  public:\
    inline operator T() const {return (m_data);}\
    inline const T& operator = (const T value){m_data = value; return m_data;}\
    inline const T& operator += (const T value){m_data += value; return m_data;}\
    inline const T& operator -= (const T value){m_data -= value; return m_data;}\
    inline const T& operator *= (const T value){m_data *= value; return m_data;}\
    inline const T& operator /= (const T value){m_data /= value; return m_data;}\
    virtual bool IsParameter(){return (true);}\
    virtual bool Parse(const char * string){m_data = P(string);}\
  };\

//define classes Parameter8,Parameter16,Parameter32,ParmeterF

DEFINE_PARAMETER(Parameter8,byte,Socket8,atoi);
DEFINE_PARAMETER(Parameter16,short,Socket16,atoi);
DEFINE_PARAMETER(Parameter32,long,Socket32,atoi);
DEFINE_PARAMETER(ParameterF,float,SocketF,atof);



#undef DEFINE_PARAMETER

#ifdef DOXYDOC //a fudge to document the classes created from the following #define

///Plugs of various types
/**Plugs encapsulate acquiring and using sockets exposed
by other modules. They can be read just like their data type.
To connect a Plug to another module's socket use this syntax
in your module's Init() function:
\code
 FooPlug.Connect("socket name");
\endcode
To access the data, simply read it like any normal variable.
Say you have a PlugF called FooPlug,
this code would work fine:
\code
float f = FooPlug;
\endcode
NOTE: It is a very good idea to check IsValid() after connecting.
The socket may not exist or may be of the wrong data type!
\code
 FooPlug.Connect("socket name");
 if(!FooPlug.IsValid())
 {
   Show an error message or reduce functionality so you don't need this value.
 }
\endcode
If you read a Plug that is not valid the returned value is indeterminate.

Writing to a plug that is not valid has no effect

Plugs are type safe so for examaple a PlugF will only connect to
a SocketF or ParameterF.


Currently defined are:
\li Plug8 (8 bit unsigned)
\li Plug16 (16 bit signed)
\li Plug32 (32 bit signed)
\li PlugF (Floating point)
*/

template<class T>
class PlugT
{
  public:
///Get the connected socket value
    inline operator T() const;
///Assignment from another plug
    inline void operator = (const PlugT& ptr);
///Assignment
    inline void operator = (const T value);
///Assignment by addition
    inline const T& operator += (const T value);
///Assignment by subtraction
    inline const T& operator -= (const T value);
///Assignment by multiplication
    inline const T& operator *= (const T value);
///Assignment by division
    inline const T& operator /= (const T value);
///Get a pointer to the socket data
    inline T * GetPointer();
///Are we connected
/**Returns true if the plug is connected to a valid socket
*/
    inline bool IsConnected();
///Connect to a socket
/**If the socket exists and is of the correct type then
Connect to it.
Returns true if the connection was successful.
*/
    bool Connect(const char * name)
///Disconnect and invalidate this plug
    void Invalidate();
}

#endif



#define DEFINE_PLUG(C,T,D)\
  class C\
  {\
  public:\
    C(){m_socket = NULL;}\
    inline operator D() const {return (*m_socket);}\
    inline void operator = (const C& ptr){if(m_socket)*m_socket = *ptr.m_socket;}\
    inline void operator = (const D value){if(m_socket)*m_socket = value;}\
    inline const T& operator += (const D value){*m_socket += value; return *m_socket;}\
    inline const T& operator -= (const D value){*m_socket -= value; return *m_socket;}\
    inline const T& operator *= (const D value){*m_socket *= value; return *m_socket;}\
    inline const T& operator /= (const D value){*m_socket /= value; return *m_socket;}\
    inline D * GetPointer(){return (m_socket->GetPointer());}\
    inline bool IsConnected(){return(m_socket != NULL);}\
    bool Connect(const char * name){\
      m_socket = (T*)g_moduleManager.GetSocket(name,#T);\
      return(IsConnected());\
    }\
    void Invalidate(){m_socket = NULL;}\
  private:\
    T * m_socket;\
  };


DEFINE_PLUG(Plug8,Socket8,byte);
DEFINE_PLUG(Plug16,Socket16,short);
DEFINE_PLUG(Plug32,Socket32,long);
DEFINE_PLUG(PlugF,SocketF,float);
#undef DEFINE_PLUG


#define DEFINE_SPLICE(C,T,D)\
  class C : public T\
  {\
  public:\
    C(){m_data = 0;\
        m_socket = NULL;\
    }\
    inline operator D() const {return (*m_socket);}\
    inline void operator = (const D value){m_data = value;}\
    inline const D& operator += (const D value){m_data = *m_socket + value; return m_data;}\
    inline const D& operator -= (const D value){m_data = *m_socket - value; return m_data;}\
    inline const D& operator *= (const D value){m_data = *m_socket * value; return m_data;}\
    inline const D& operator /= (const D value){m_data = *m_socket / value; return m_data;}\
    inline void PassThrough(){m_data = *m_socket;}\
    inline bool IsConnected(){return(m_socket != NULL);}\
    bool Connect(const char * name){\
      m_socket = (T*)g_moduleManager.GetSocket(name,#T);\
      if(m_socket) g_moduleManager.ReplaceSocket(m_socket,this);\
      return(IsConnected());\
    }\
    void Invalidate(){m_socket = NULL;}\
  private:\
    T * m_socket;\
  };


DEFINE_SPLICE(Splice8,Socket8,byte);
DEFINE_SPLICE(Splice16,Socket16,short);
DEFINE_SPLICE(Splice32,Socket32,long);
DEFINE_SPLICE(SpliceF,SocketF,float);
#undef DEFINE_SPLICE


#endif //#ifndef SOCKETS_H_INCLUDED


